<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
		<link rel="Stylesheet" type="text/css" href="doc.css" />
		<title>Tutorial: EMF Model Transaction Workspace Integration</title>
	</head>
	<body>
		<h1><a name="top">Tutorial: EMF Model Transaction Workspace Integration</a></h1>

		<h2>Contents</h2>
		<ul>
			<li><a href="#overview">Overview</a></li>
			<li><a href="#refs">References</a></li>
			<li><a href="#intro">Introduction</a></li>
			<li><a href="#editingDomainRegistration">Registering the Editing Domain</a></li>
			<li><a href="#listenerRegistration">Registering a Resource Load Listener</a></li>
			<li><a href="#workspaceSynchronized">Synchronizing with Workspace Changes</a></li>
			<li><a href="#mandatoryTransactionUse">Every Read and Write Must be in a Transaction</a></li>
			<li><a href="#managingUndoContext">Managing the Undo Context</a></li>
			<li><a href="#summary">Summary</a></li>
		</ul>

		<h2><a name="overview">Overview</a></h2>
		<p>
			The EMF Model Transaction workspace integration allows one to integrate the use of the transaction layer
			with the eclipse undoable operations framework. This will help the transaction layer to
			coexist with other eclipse UI components and share operations that modify transactional
			editing domains as well as other objects.
		</p>
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="refs">References</a></h2>
		<p>
			This tutorial assumes that the reader has a knowledge of EMF, and the eclipse PDE development
			environment. It is essential that the reader understands the eclipse undoable operations framework. 
			It assumes that the reader has already read the transaction tutorial and understood those concepts.
		</p>
		<p>
			For reference, the full <a href="../references/examples/workspaceExample.html">example</a> for this tutorial is available.
		</p>
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="intro">Introduction</a></h2>
		<p>
			In order to demonstrate transaction workspace integration we will be making use of the library metamodel. This metamodel
			is a variant of the standard EMF example metamodel used in many of its tutorials.
		</p>
		<p>
			The goal of this tutorial is to create a reflective editor that manages a single resource
			within a transactional editing domain. Whenever a resource is loaded in the editing domain
			a new editor is opened. When an instance of the editor is opened, it finds its editing domain
			and loads the resource. Each editor will have its own undo/redo actions that reflect only the
			changes made to its resource.
		</p>
		<p>
			Our editor will be extending the EXTLibrary editor generated for that metamodel. Much of
			the UI work has been done and needs only to be adapted to use the undoable operations
			framework and transactional editing domains.
		</p>
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="editingDomainRegistration">Registering the Editing Domain</a></h2>
		<p>
			As a first step, we will declare a transactional editing domain that will be shared among all of the
			editor instances. Whenever an editor opens, it will retrieve this editing domain to load and manage its
			resource.
		</p>
		
<pre class="codeblock">
  &lt;extension
        point=&quot;org.eclipse.emf.transaction.editingDomains&quot;&gt;
     &lt;editingDomain
           factory=&quot;org.eclipse.emf.workspace.examples.extlibrary.domain.EXTLibraryEditingDomainFactory&quot;
           id=&quot;org.eclipse.emf.workspace.examples.LibraryEditingDomain&quot;/&gt;
  &lt;/extension&gt;
</pre>

		<p>
			The editing domain factory is slightly different than the one outlined in the transaction tutorial.
			Instead, it will be constructing a special workspace integrated transactional editing domain. A special
			exception handler is registered that will present a dialog to the user when errors occur within
			transactions (e.g. roll backs due to validation failure).
		</p>

<pre class="codeblock">
public class EXTLibraryEditingDomainFactory implements TransactionalEditingDomain.Factory {
   public TransactionalEditingDomain createEditingDomain() {
      // create an editing domain with a default resource set implementation
      //    and delegating command execution to the default (workbench)
      //    operation history
      TransactionalEditingDomain result = WorkspaceEditingDomainFactory.INSTANCE.createEditingDomain();
      
      // add an exception handler to the editing domain's command stack
      ((TransactionalCommandStack) result.getCommandStack()).setExceptionHandler(
            new CommandStackExceptionHandler());
      
      return result;
   }

   public TransactionalEditingDomain createEditingDomain(ResourceSet rset) {
      // not used when initializing editing domain from extension point
      return null;
   }

   public TransactionalEditingDomain getEditingDomain(ResourceSet rset) {
      // not used when initializing editing domain from extension point
      return null;
   }
}
</pre>
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="listenerRegistration">Registering a Resource Load Listener</a></h2>
		<p>
			We want to have our special editor opened whenever a new resource is loaded.
			Resources may load explicity or implicitly through proxy resolution. The
			listener should be registered against the listeners extension point so that
			it will be automatically installed on the editing domain whenever it is
			constructed.
		</p>

<pre class="codeblock">
  &lt;extension
        point=&quot;org.eclipse.emf.transaction.listeners&quot;&gt;
     &lt;listener class=&quot;org.eclipse.emf.workspace.examples.extlibrary.presentation.ResourceLoadedListener&quot;&gt;
        &lt;editingDomain id=&quot;org.eclipse.emf.workspace.examples.LibraryEditingDomain&quot;/&gt;
     &lt;/listener&gt;
  &lt;/extension&gt;  
</pre>

		<p>
			If a resource has been loaded the listener finds the IFile for the resource's URI 
			and requests the workbench to open an editor for that file. Otherwise, if the
			resource has been unloaded it finds any open editor instance for that file and closes
			it.
		</p>
		
<pre class="codeblock">
public class ResourceLoadedListener extends DemultiplexingListener {

...
   protected void handleNotification(TransactionalEditingDomain domain, Notification notification) {
      if (ignoredResources.contains(notification.getNotifier())) {
         // skip any resource that we are supposed to ignore
         return;
      }
      
      if (notification.getNewBooleanValue() &amp;&amp; !notification.getOldBooleanValue()) {
         // a resource has been loaded that was not loaded before.  Open an editor
         final IFile file = WorkspaceSynchronizer.getFile(
               (Resource) notification.getNotifier());
         
         if (file != null) {
            Display.getDefault().asyncExec(new Runnable() {
               public void run() {
                  try {
                     IWorkbenchPage page = getActivePage();
                     
                     if (page != null) {
                        IEditorPart activeEditor = page.getActiveEditor();
                        
                        page.openEditor(
                              new FileEditorInput(file),
                              "org.eclipse.emf.workspace.examples.extlibrary.presentation.EXTLibraryEditorID",
                              false);
                        
                        // restore the previously active editor to active
                        //    state
                        if (activeEditor != null) {
                           page.activate(activeEditor);
                        }
                     }
                  } catch (PartInitException e) {
                     EXTLibraryEditorPlugin.getPlugin().log(e.getStatus());
                  }
               }});
         }
      } else if (!notification.getNewBooleanValue() &amp;&amp; notification.getOldBooleanValue()) {
         // a resource has been unloaded that was  loaded before.  Close
         //    the editor, if any
         final IFile file = WorkspaceSynchronizer.getFile(
               (Resource) notification.getNotifier());
         
         if (file != null) {
            Display.getDefault().asyncExec(new Runnable() {
               public void run() {
                  IWorkbenchPage page = getActivePage();
                  
                  if (page != null) {
                     IEditorReference[] editors = page.findEditors(
                           new FileEditorInput(file),
                           "org.eclipse.emf.workspace.examples.extlibrary.presentation.EXTLibraryEditorID",
                           IWorkbenchPage.MATCH_ID | IWorkbenchPage.MATCH_INPUT);
                     
                     page.closeEditors(editors, false);
                  }
               }});
         }
      }
   }   
...
}
</pre>
		
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="workspaceSynchronized">Synchronizing with Workspace Changes</a></h2>
		<p>
			The user could make changes to the files in their workspace and we must keep
			up-to-date with those changes if they affect our loaded resources. A workspace
			synchronizer is used to force the closing of editors whose resources have been
			deleted, and the reloading of resources that have changed.
		</p>
		
<pre class="codeblock">
public void init(IEditorSite site, IEditorInput editorInput) {
   setSite(site);
   setInputWithNotify(editorInput);
   setPartName(editorInput.getName());
   site.setSelectionProvider(this);
   site.getPage().addPartListener(partListener);
   
   <b>workspaceSynchronizer = new WorkspaceSynchronizer(
         (TransactionalEditingDomain) editingDomain,
         createSynchronizationDelegate());</b>
}

...

private WorkspaceSynchronizer.Delegate createSynchronizationDelegate() {
   return new WorkspaceSynchronizer.Delegate() {
      public boolean handleResourceDeleted(Resource resource) {
         if ((resource == getResource()) &amp;&amp; !isDirty()) {
            // just close now without prompt
            getSite().getShell().getDisplay().asyncExec
               (new Runnable() {
                   public void run() {
                      getSite().getPage().closeEditor(EXTLibraryEditor.this, false);
                      EXTLibraryEditor.this.dispose();
                   }
                });
         } else {
            removedResources.add(resource);
         }
         
         return true;
      }
      
      public boolean handleResourceChanged(Resource resource) {
         changedResources.add(resource);
         
         return true;
      }
      
      public boolean handleResourceMoved(Resource resource, URI newURI) {
         movedResources.put(resource, newURI);
         
         return true;
      }
      
      public void dispose() {
         removedResources.clear();
         changedResources.clear();
         movedResources.clear();
      }};
}
</pre>

		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="mandatoryTransactionUse">Every Read and Write Must be in a Transaction</a></h2>
		<p>
			Because of the multi-threaded nature of transactional editing domains, every
			read and write must be performed in a transaction. EMF has already made use
			of a command stack to allow it to manage undo and redo of changes. If changes
			could be made using the reflective editor that weren't implemented in terms of
			an EMF command that was executed on their command stack then they would not have
			been able to ensure the sanity of their undo/redo actions. It is therefore reasonable
			to assume that all changes that can be made using the reflective editor will be
			executed as EMF commands on the command stack. 
		</p>
		<p>
			Our editing domain factory produces
			transactional editing domains with a special command stack that will first wrap each
			EMF command into an EMFCommandOperation on the operation history. It opens a write
			transaction during the course of the command execution. Writing should be trivially
			solved due to the transactional editing domain's command stack implementation.
		</p>
		<p>
			Reading is a more complicated problem because when the reflective editor reads information
			from the editing domain, it does not assume that any particular command or method call
			has to be made to allow the read. We are forced to override most of the cases where reading
			is done by the editor.
		</p>
		<p>
			First there is the main tree viewer for the editor. It updates its content and labels
			by reading data from the editing domain. It is overrided so that it uses a transactional
			adapter factory:
		</p>
		
<pre class="codeblock">
public void createPartControl(Composite parent) {
	// Creates the model from the editor input
	//
	createModel();

	Tree tree = new Tree(parent, SWT.MULTI);
	selectionViewer = new TreeViewer(tree);

	selectionViewer.setContentProvider(
			<b>new TransactionalAdapterFactoryContentProvider(
				(TransactionalEditingDomain) getEditingDomain(), adapterFactory)</b>);

	selectionViewer.setLabelProvider(<b>new TransactionalAdapterFactoryLabelProvider(
			(TransactionalEditingDomain) getEditingDomain(), adapterFactory)</b>);
	
	// unlike other EMF editors, I edit only a single resource, not a resource set
	selectionViewer.setInput(getResource());

	new AdapterFactoryTreeEditor(selectionViewer.getTree(), adapterFactory);

	createContextMenuFor(selectionViewer);
}
</pre>
			
		<p>
			There is also the property sheet that needs to update its contents by performing read operations
			 on the editing domain.
		</p>
		
<pre class="codeblock">
public IPropertySheetPage getPropertySheetPage() {
   if (propertySheetPage == null) {
      propertySheetPage =
         new <b>ExtendedPropertySheetPage</b>(editingDomain) {
            public void setSelectionToViewer(List selection) {
               EXTLibraryEditor.this.setSelectionToViewer(selection);
               EXTLibraryEditor.this.setFocus();
            }

            public void setActionBars(IActionBars actionBars) {
               super.setActionBars(actionBars);
               getActionBarContributor().shareGlobalActions(this, actionBars);
            }
         };
      propertySheetPage.setPropertySourceProvider(
            new <b>TransactionalAdapterFactoryContentProvider</b>(
               (TransactionalEditingDomain) getEditingDomain(), adapterFactory));
   }

   return propertySheetPage;
}
</pre>

		<p>
			Saving and loading a resource requires appropriate transactions.
		</p>
		
<pre class="codeblock">
public void doSave(IProgressMonitor progressMonitor) {
   WorkspaceModifyOperation operation =
      new WorkspaceModifyOperation() {
         // This is the method that gets invoked when the operation runs.
         //
         public void execute(IProgressMonitor monitor) {
            try {
               <b>((TransactionalEditingDomain) getEditingDomain())
                  .runExclusive(</b>new Runnable() {
                  public void run() {
                     try {
                        // Save the resource to the file system.
                        //
                        Resource savedResource = getResource();
                        savedResources.add(savedResource);
                        savedResource.save(Collections.EMPTY_MAP);
                     }
                     catch (Exception exception) {
                        EXTLibraryEditorPlugin.INSTANCE.log(exception);
                     }
                  }});
            }
            catch (Exception exception) {
               EXTLibraryEditorPlugin.INSTANCE.log(exception);
            }
         }
      };
   ...
}
</pre>
		
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="managingUndoContext">Managing the Undo Context</a></h2>
		<p>
			We would like each editor to maintain its own undo context. From that
			context it will be determined which operations should show up on our
			editor's undo/redo menu. Basically, we want to have only those operations
			that affect the editor's resource have our context applied to them. We do
			this by creating an operation history listener:
		</p>
		
<pre class="codeblock">
public EXTLibraryEditor() {
   super();

   // Create an adapter factory that yields item providers.
   //
   List factories = new ArrayList();
   factories.add(new ResourceItemProviderAdapterFactory());
   factories.add(new EXTLibraryItemProviderAdapterFactory());
   factories.add(new ReflectiveItemProviderAdapterFactory());

   adapterFactory = new ComposedAdapterFactory(factories);

   // Get the registered workbench editing domain.
   //
   editingDomain = (AdapterFactoryEditingDomain) 
         TransactionalEditingDomain.Registry.INSTANCE.getEditingDomain(
            "org.eclipse.emf.workspace.examples.LibraryEditingDomain");
   <b>undoContext = new ObjectUndoContext(
         this,
         EXTLibraryEditorPlugin.getPlugin()
            .getString("_UI_EXTLibraryEditor_label"));
   getOperationHistory().addOperationHistoryListener(historyListener);</b>
}

private IOperationHistoryListener historyListener = new IOperationHistoryListener() {
   public void historyNotification(final OperationHistoryEvent event) {
      if (event.getEventType() == OperationHistoryEvent.DONE) {
         Set affectedResources = ResourceUndoContext.getAffectedResources(
               event.getOperation());
         
         if (affectedResources.contains(getResource())) {
            final IUndoableOperation operation = event.getOperation();
            
            // remove the default undo context so that we can have
            //     independent undo/redo of independent resource changes
            operation.removeContext(((IWorkspaceCommandStack)
                  getEditingDomain().getCommandStack()).getDefaultUndoContext());
            
            // add our undo context to populate our undo menu
            <b>operation.addContext(getUndoContext());</b>
            
            getSite().getShell().getDisplay().asyncExec(new Runnable() {
               public void run() {
                  dirty = true;
                  firePropertyChange(IEditorPart.PROP_DIRTY);

                  // Try to select the affected objects.
                  //
                  if (operation instanceof EMFCommandOperation) {
                     Command command = ((EMFCommandOperation) operation).getCommand();
                     
                     if (command != null) {
                        setSelectionToViewer(command
                              .getAffectedObjects());
                     }
                  }
                  
                  if (propertySheetPage != null) {
                     propertySheetPage.refresh();
                  }
               }
            });
         }
      }
   }};
</pre>		
		
		<p>
			In order for the outside world (including our undo/redo actions) to know about our
			context we need to provide this information by being adaptable to our own undo context.
		</p>
		
<pre class="codeblock">
public Object getAdapter(Class key) {
	if (key.equals(IContentOutlinePage.class)) {
		return getContentOutlinePage();
	}
	else if (key.equals(IPropertySheetPage.class)) {
		return getPropertySheetPage();
	}
	else if (key.equals(IGotoMarker.class)) {
		return this;
	}
	<b>else if (key.equals(IUndoContext.class)) {
		// used by undo/redo actions to get their undo context
		return undoContext;
	}</b>
	else {
		return super.getAdapter(key);
	}
}
</pre>
		
		<p>
			Finally, we must override the default EMF undo/redo actions to use the operation history
			rather than the command stack. In order to do this we use wrappers for operation
			history UndoActionHandler/RedoActionHandlers to EMF undo/redo actions in our action bar
			contributor class:
		</p>
		
<pre class="codeblock">
public void init(IActionBars actionBars) {
	super.init(actionBars);

    ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
    
	// override the superclass implementation of these actions
    undoAction = <b>new UndoActionWrapper();</b>
    undoAction.setImageDescriptor(
    		sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_UNDO));
    actionBars.setGlobalActionHandler(
    		ActionFactory.UNDO.getId(), undoAction);

    redoAction = <b>new RedoActionWrapper();</b>
    redoAction.setImageDescriptor(
    		sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
    actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), redoAction);
}
</pre>

		<p>
			Each action wrapper is able to determine the editor's undo context by adapting the
			active editor part to an IUndoContext (see above code for getAdapter()). With this information they
			can determine which operations should show up in our editor's undo/redo actions.
		</p>
		
		<p class="small">[<a href="#top">back to top</a>]</p>

		<h2><a name="summary">Summary</a></h2>
		<p>
			In this tutorial, we did the following:
			<ol>
				<li>Registered a workspace integrated transactional editing domain for our editor instances to share</li>
				<li>Created and registered a listener responsible for opening new editor instances whenever a resource is loaded</li>
				<li>Instantiated a workspace synchronizer to keep our editing domain synchronized with changes in the workspace</li>
				<li>Ensured that every read and write that occurs in the editor will be performed inside an appropriate transaction</li>
				<li>Looked at various helpful classes provided by the transaction and workspace integration layers to override basic EMF
				    classes and adapt them for use in transactional editing domains</li>
				<li>Managed the undo context for our editors so that they show only undo/redo for operations that affect their resource</li>
				<li>Overrode an EMF generated reflective editor to integrate it into transactional editing domains and the eclipse
				    operations framework</li>
			</ol>
		</p>
		<p class="small">[<a href="#top">back to top</a>]</p>

		<hr />

		<p>
			<a href="https://www.eclipse.org/legal/epl-2.0/">Copyright (c) 2006 IBM Corporation and others.</a>
		</p>
	</body>
</html>
